package com.example.supplychainbackend.service.Impl;

import com.example.supplychainbackend.controller.dto.blockchain.UploadInfo;
import com.example.supplychainbackend.controller.dto.design.BOMDto;
import com.example.supplychainbackend.controller.dto.plan.*;
import com.example.supplychainbackend.controller.dto.plan.cost.CostInfoDto;
import com.example.supplychainbackend.controller.dto.production.ProductionSchedulingDto;
import com.example.supplychainbackend.controller.dto.production.ProductionWorkorderDto;
import com.example.supplychainbackend.controller.dto.sales.ContractDto;
import com.example.supplychainbackend.controller.dto.sales.DeliverableDto;
import com.example.supplychainbackend.controller.dto.sales.OrderDetailDto;
import com.example.supplychainbackend.controller.dto.warehouse.OutboundBillDto;
import com.example.supplychainbackend.dao.plan.PlanOrderDao;
import com.example.supplychainbackend.entity.plan.*;
import com.example.supplychainbackend.entity.plan.cost.CostInfoEntity;
import com.example.supplychainbackend.mapper.PlanMapper;
import com.example.supplychainbackend.service.*;
import com.example.supplychainbackend.tool.DateTimeUtils;
import jakarta.validation.constraints.NotBlank;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

@Service
@RequiredArgsConstructor
public class PlanServiceImpl implements PlanService {
    private final PlanMapper mapper;
    private final PlanOrderDao dao;

    private final SalesService salesService;
    private final DesignService designService;

    private final BlockchainService blockchainService;

    @Override
    public PlanOrderDetailDto getPlanOrderDetail(String mo) {
        PlanOrderEntity entity = dao.findByWorkOrderNumber(mo);
        if (entity == null) throw new RuntimeException("plan order " + mo + " not found");

        // desc
        ProductionDescDto descInfo = new ProductionDescDto();

        OrderDetailDto so = salesService.getOrder(entity.getSalesOrderNo());
        ContractDto co = salesService.getContract(so.getContractNumber());

        descInfo.setSalesOrderNumber(entity.getSalesOrderNo());
        descInfo.setSalesOrderName(so.getSalesOrderName());
        descInfo.setContractNumber(co.getContractNumber());
        descInfo.setContractName(co.getContractName());
        descInfo.setCustomerName(co.getCustomerName());
        descInfo.setDeliveryDate(co.getRequiredDeliveryDate());
        descInfo.setDeliveryGroupName(co.getProductGroup());
        descInfo.setMaterialCode(entity.getProductCode());

        // schedule
        ProductionScheduleEntity scheduleInfoEntity = entity.getProductionSchedule();
        ProductionScheduleDto scheduleInfo = mapper.toProdScheDto(scheduleInfoEntity);

        // prep
        List<MaterialInfoEntity> materialInfoEntities = entity.getMaterialItems();
        List<MaterialInfoDto> matItemInfos = mapper.toMatInfoDtoList(materialInfoEntities);

        List<BOMInfoEntity> bomInfoEntities = entity.getBomItems();
        List<BOMInfoDto> bomInfos = mapper.toBomDtoList(bomInfoEntities);

        List<MatchingPlanEntity> matchingPlanEntities = entity.getMatchingPlans();
        List<MatchingPlanDto> matchingPlanDtos = mapper.toMatchPlanDtoList(matchingPlanEntities);

        PreparationDto preparationInfo = new PreparationDto(bomInfos,matItemInfos,matchingPlanDtos);

        // cost
        CostInfoEntity costInfoEntity = entity.getCost();
        CostInfoDto costInfo = mapper.toCostInfoDto(costInfoEntity);

        // completeNote
        String compNote = entity.getCompletionNote();

        return new PlanOrderDetailDto(entity.getWorkOrderNumber(), descInfo, scheduleInfo, preparationInfo,
                compNote, costInfo,
                entity.getUserName(), entity.getEnterpriseName(),entity.getOnChain(),entity.getTransactionId(),
                entity.getCommitTime(),entity.getUserId(),entity.getEnterpriseId());
    }

    @Override
    public PlanOrderDetailDto updatePlanOrder(String mo, PlanOrderDetailDto form) {
        PlanOrderEntity originEntity = dao.findByWorkOrderNumber(mo);
        PlanOrderEntity entity = mapper.toPlanOrderEntity(form);
        entity.setWorkOrderNumber(mo);

        // update don't change schedule
        ProductionScheduleEntity originSchedule = originEntity.getProductionSchedule();
//        ProductionScheduleEntity updateSchedule = entity.getProductionSchedule();
//        originSchedule.setRequiredQuantity(updateSchedule.getRequiredQuantity());
//        originSchedule.setPlannedStartDate(updateSchedule.getPlannedStartDate());
//        originSchedule.setPlannedEndDate(updateSchedule.getPlannedEndDate());
//        originSchedule.setStorageLocation(updateSchedule.getStorageLocation());
        entity.setProductionSchedule(originSchedule);

        ProductionDescDto descInfo = form.getProductionDesc();
        entity.setSalesOrderNo(descInfo.getSalesOrderNumber());
        entity.setProductCode(descInfo.getMaterialCode());
//        entity.setWorkOrderStatus(wo);
//        entity.setStorageStatus(0);
//        entity.setWorkOrderType(0);
//        entity.setProductionStatus(0);
//        entity.setReportStatus(0);

//        entity.setProductionSchedule(form.getProductionSchedule());
//        entity.setCost(form.getCost());
        PreparationDto preparationDto = form.getPreparation();
        entity.setBomItems(mapper.toBomEntityList(preparationDto.getBomItems()));
        entity.setMaterialItems(mapper.toMatInfoEntityList(preparationDto.getMaterialItems()));
        entity.setMatchingPlans(mapper.toMatchPlanEntityList(preparationDto.getMatchingPlans()));
        entity = dao.save(entity);
        return getPlanOrderDetail(mo);
    }

    @Override
    public String createPlanOrder(PlanOrderDetailDto form) {
        PlanOrderEntity entity = mapper.toPlanOrderEntity(form);
        ProductionDescDto descInfo = form.getProductionDesc();
        entity.setSalesOrderNo(descInfo.getSalesOrderNumber());
        entity.setProductCode(descInfo.getMaterialCode());

        BOMDto productBom = designService.getBOMByCode(entity.getProductCode());
        entity.setProductName(productBom.getMaterialName());
        entity.setWorkOrderStatus(1);
        entity.setStorageStatus(2);
//        entity.setWorkOrderType(0);
        entity.setProductionStatus(2);
        entity.setReportStatus(2);

//        entity.setProductionSchedule(form.getProductionSchedule());
//        entity.setCost(form.getCost());
        PreparationDto preparationDto = form.getPreparation();
        if (preparationDto!=null){
            entity.setBomItems(mapper.toBomEntityList(preparationDto.getBomItems()));
            entity.setMaterialItems(mapper.toMatInfoEntityList(preparationDto.getMaterialItems()));
            entity.setMatchingPlans(mapper.toMatchPlanEntityList(preparationDto.getMatchingPlans()));
        }
        entity.setOnChain(false);
        entity = dao.save(entity);
        return entity.getWorkOrderNumber();
    }

    @Override
    public List<PlanOrderDto> getPlanOrders() {
        List<PlanOrderEntity> entities = dao.findAll();
        List<PlanOrderDto> dtos = new ArrayList<>();
        for (PlanOrderEntity entity:
             entities) {
            PlanOrderDetailDto detailDto = getPlanOrderDetail(entity.getWorkOrderNumber());
            PlanOrderDto dto = mapper.toPlanOrderDto(entity);
            ProductionDescDto desc = detailDto.getProductionDesc();
            dto.setContractName(desc.getContractName());
            dto.setCustomerName(desc.getCustomerName());
            dto.setContractNo(desc.getContractNumber());
            dto.setDeliveryDate(desc.getDeliveryDate());
            dto.setDeliveryGroupNo(desc.getDeliveryGroupNumber());
            dto.setSalesOrderName(desc.getSalesOrderName());
            dto.setCompletionStatus("COMPLETED");
            dto.setStorageStatus(2);
            dto.setProductionStatus(2);
            dto.setReportStatus(2);
            dto.setWorkOrderStatus(1);

            dto.setStorageQuantity(detailDto.getProductionSchedule().getStorageQuantity());
            dto.setProcessRoute(detailDto.getProductionSchedule().getProcessRoute());
            dto.setArea(detailDto.getProductionSchedule().getArea());
            dto.setProductionLine(detailDto.getProductionSchedule().getProductionLine());

            dto.setPlannedStartDate(detailDto.getProductionSchedule().getPlannedStartDate());
            dto.setPlannedEndDate(detailDto.getProductionSchedule().getPlannedEndDate());
            dto.setActualStartDate(detailDto.getProductionSchedule().getActualStartDate());
            dto.setActualEndDate(detailDto.getProductionSchedule().getActualEndDate());

            dtos.add(dto);
        }
        return dtos;
    }

    @Override
    public Long deletePlanOrder(String mo) {
        return dao.deleteByWorkOrderNumber(mo);
    }

    @Override
    public PlanOrderDetailDto reportToPlanOrder(ProductionWorkorderDto wo) {
        PlanOrderEntity entity = dao.findByWorkOrderNumber(wo.getPlanOrderNo());

        ProductionScheduleEntity scheduleEntity = entity.getProductionSchedule();
        int quantity = scheduleEntity.getProductionQuantity() + wo.getReportedQuantity();
        scheduleEntity.setProductionQuantity(quantity);
        scheduleEntity.setCompleteRate(quantity / scheduleEntity.getRequiredQuantity());

        if (quantity >= scheduleEntity.getRequiredQuantity()){
            entity.setProductionStatus(2);
            entity.setReportStatus(2);
        }else{
            entity.setProductionStatus(1);
            entity.setReportStatus(1);
            if (!Objects.equals(entity.getCompletionStatus(), "COMPLETED"))
                entity.setCompletionStatus("PARTIALLY_COMPLETED");
        }
        entity.setProductionSchedule(scheduleEntity);
        entity = dao.save(entity);
        return this.getPlanOrderDetail(entity.getWorkOrderNumber());
    }

    @Override
    public PlanOrderDetailDto finishPlanOrder(String mo, String status) {
        PlanOrderEntity entity = dao.findByWorkOrderNumber(mo);
        entity.setCompletionStatus(status);

        ProductionScheduleEntity scheduleEntity = entity.getProductionSchedule();
        scheduleEntity.setActualEndDate(DateTimeUtils.getCurrentDateTime());

        entity.setProductionSchedule(scheduleEntity);
        dao.save(entity);
        return getPlanOrderDetail(mo);
    }

    @Override
    public UploadInfo uploadPlanOrder(String id) {
        PlanOrderDetailDto planOrderDetail = this.getPlanOrderDetail(id);
        if (null!=planOrderDetail){
            UploadInfo txInfo = blockchainService.uploadPlanOrder(id, planOrderDetail.toString());
            planOrderDetail.setOnChain(true);
            planOrderDetail.setCommitTime(txInfo.getCommitTime());
            planOrderDetail.setTransactionId(txInfo.getTransactionId());
            this.updatePlanOrder(id,planOrderDetail);
            return txInfo;
        }
        throw new RuntimeException("plan order not found");
    }

    @Override
    public List<PlanOrderDto> getPlanOrdersByEid(String eid) {
        List<PlanOrderDto> list = getPlanOrders();
        List<PlanOrderDto> eList = new ArrayList<>();
        for (PlanOrderDto dto:
             list) {
            if (dto.getEnterpriseId().equals(eid)){
                eList.add(dto);
            }
        }
        return eList;
    }
}
